#include <Servo.h>  //调用舵机库
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>
#include <ArduinoJson.h>
WiFiClient myesp8266Client;
PubSubClient myclient(myesp8266Client);
Servo myservo;  // 定义舵机对象
#define WIFI_SSID         "GT1"       //wifi名称
#define WIFI_PASSWORD     "8123456788"       //wifi密码

#define device_id         "66126f4c2ccc1a58388021bf_JIAJ2"       //注册设备的ID和密钥
#define secret            "12345678"    
//MQTT三元组
#define ClientId          "66126f4c2ccc1a58388021bf_JIAJ2_0_0_2024040709"
#define Username          "66126f4c2ccc1a58388021bf_JIAJ2"
#define Password          "fe1a41a613defec50c4f88ca442b9a9b91d8da15ab26015cfb1a862acc365992"

#define MQTT_Address "d9a517dbcf.st1.iotda-device.cn-north-4.myhuaweicloud.com"
#define MQTT_Port 1883

#define Iot_link_Body_Format "{\"services\":[{\"service_id\":\"温湿度\",\"properties\":{%s" //Dev_data换成你产品里的服务ID
//{"services":[{"service_id":"Dev_data","properties":{"temp": 39}}]}
//设备属性上报
#define Iot_link_MQTT_Topic_Report "$oc/devices/"device_id"/sys/properties/report"
//接收平台下发的命令
#define Iot_link_MQTT_Topic_Commands "$oc/devices/"device_id"/sys/commands/#" 
//设备相应平台的命令
#define Iot_link_MQTT_Topic_CommandsRes "$oc/devices/"device_id"/sys/commands/response/request_id="
#define Aout A0 //MQ-2 AO 接 Arduino Uno A0
const int LEDPIN =2;
const int fen =4; 
long lastMsg = 0;
int yanw = 0; //临时变量，存储A0读取的数据

 

void setup() {
  Serial.begin(115200);//定义波特率
  pinMode(Aout, INPUT);//定义A0为INPUT模式
  pinMode(fen, OUTPUT);
  pinMode(LEDPIN, OUTPUT);
  myservo.attach(5);// 设置舵机控制针脚接数字5引脚
  WIFI_Init();                //wifi初始化
  MQTT_Init();                //云平台连接初始化
}

 

void loop() {
   if (!myclient.connected()){         //连接成功则不进行MQTT_Init();
    MQTT_Init();
  } 
  else myclient.loop();               //连接成则检查是否有数据上传，下发

  long now = millis();
  
  if (now - lastMsg > 5000) {
    lastMsg = now;
    yanw = analogRead(Aout); //读取A0的模拟数据
    if(yanw>520){
      digitalWrite(fen,HIGH);
      }else{
        digitalWrite(fen,LOW);
        }
    MQTT_sjsq();
  }
 
}
void MQTT_sjsq()
{
  char properties[32];
  char jsonBuf[128];

 sprintf(properties,"\"烟雾\":%d}}]}",yanw);                 //data_temp变量的值格式化为一个JSON字符串，并存储在properties字符串中
 
  sprintf(jsonBuf,Iot_link_Body_Format,properties);          //将properties字符串插入到另一个JSON模板Iot_link_Body_Format中。
  
 myclient.publish(Iot_link_MQTT_Topic_Report, jsonBuf);      //Iot_link_Body_Format中的占位符将被properties中的JSON字符串替换。jsonBuf存储最终生成的完整JSON消息。

  Serial.println(Iot_link_MQTT_Topic_Report);               //第一个参数Iot_link_MQTT_Topic_Report是MQTT主题（Topic），指定了消息将被发布到的频道。

  Serial.println(jsonBuf);                                  //第二个参数jsonBuf是包含要发布的消息内容的字符串，这个消息遵循之前构建的JSON格式。
  Serial.println("MQTT Publish OK!");                       //publish方法将消息发送到MQTT代理服务器，然后服务器会将消息分发给订阅了该主题的所有客户端。
}
//连接wifi
void WIFI_Init()
{
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID,WIFI_PASSWORD);
    while(WiFi.status()!=WL_CONNECTED)
    {
      delay(1000);
      Serial.println("WiFi Not Connect");
    }
    Serial.println("WiFi Connected OK!");
}

//连接MQTT
void MQTT_Init()
{
  myclient.setServer(MQTT_Address,MQTT_Port);                 
  while(!myclient.connected())
  {
    myclient.connect(ClientId,Username,Password);
  }
   myclient.setCallback(callback);//设定回调方式，当ESP8266收到订阅消息时会调用此方法
  boolean res = myclient.subscribe(Iot_link_MQTT_Topic_Commands); //连接成功时订阅主题commands
  if(res != true){
     Serial.printf("mqtt subscribe topic [%s] fail\n", Iot_link_MQTT_Topic_Commands);
  }
  Serial.printf("mqtt subscribe topic [%s] ok\n", Iot_link_MQTT_Topic_Commands);

  Serial.println("MQTT Connected OK!");

}
void callback(char* topic, byte* payload, unsigned int length)
{
  String recdata="";
  Serial.printf("them:");//订阅主题
  Serial.println(topic); 
  Serial.printf("data:");//订阅内容
  for(int i=0;i<length;i++)
  {
    recdata+=(char)payload[i];
  }
  Serial.println(recdata);
  //解析JSON数据
  DynamicJsonDocument jsonBuffer(1024);
  deserializeJson(jsonBuffer,recdata);            //将recdata字符串中的JSON数据解析到之前创建的jsonBuffer对象中。
                                                  //deserializeJson函数是DynamicJson库提供的一个函数，
                                                  //用于将JSON格式的字符串转换为DynamicJsonDocument可以处理的内部数据结构。
  JsonObject obj = jsonBuffer.as<JsonObject>(); //这行代码将jsonBuffer对象转换为JsonObject类型，并将其存储在名为obj的变量中
  JsonObject obj1 = jsonBuffer.as<JsonObject>();
  String com = obj["paras"];                      //这行代码从obj对象中提取键为"paras"的值，并将其存储在名为com的String变量中
  String com1 = obj1["paras"];                                                //[]操作符用于访问JSON对象中的键值对，这里它被用来获取名为"paras"的字段的值。
                                                  //如果该字段存在，com变量将包含其字符串值；如果不存在，com将包含一个空字符串。
  Serial.printf("jie_xi_mingling_com:");//解析命令
  Serial.println(com);
  Serial.println(com1);
  deserializeJson(jsonBuffer,com);
  obj = jsonBuffer.as<JsonObject>();
  String ledcom = obj["灯开1关0"];                  //这里的test换成你命令里的名字
  String servo = obj1["门开1关0"];
  Serial.printf("jie_xi_led_mingling_ledcom:");//解析命令
  Serial.println(ledcom);
  
  //解析request id，设备响应时的topic需要包含命令的request id，且会动态变化
  char *p=topic;
  String request_id="";
  int i=0;
  while((*p++)!='\0')//获取topic长度
  {
    i++;
  }
  topic+=(i-36);    //移动指针位置
  
  for(int j=i-36;j<i;j++)//末36位为request id
    request_id+=*topic++;
  Serial.println("request_id："+request_id);
  Serial.println("mingling");//命令
  String param="{}";
  myclient.publish((Iot_link_MQTT_Topic_CommandsRes+request_id).c_str(),param.c_str());
  if(ledcom=="1")
  {
    digitalWrite(LEDPIN,HIGH);  //LED高电平触发,低电平熄灭
    Serial.println("on_led");  
  }
  else if(ledcom=="0")
  {
    digitalWrite(LEDPIN,LOW);  //LED高电平触发,低电平熄灭
    Serial.println("off_led");    
  }  
   if(servo=="1")
  {
     myservo.write(90);  delay(1000); //占空比为1.5ms的PWM信号停止1秒  /舵机高电平打开
    Serial.println("on_servo");  
  }
  else if(servo=="0")
  {
   myservo.write(0);   delay(3000); //占空比为0.5ms的PWM信号旋转约3秒时间  //LED低电平关闭
    Serial.println("off_servo");    
  }
}
